#pragma once

#include <iostream>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include<cstring>
    #include <arpa/inet.h>
#include<unistd.h>

       #include <sys/wait.h>

#include"Log.hpp"
const int defaultfd = -1;
const uint16_t defaultport = 8888;

const std::string defaultip = "0.0.0.0";
const int backlog =10 ;
Log lg ;


enum
{
    UsageError = 1,
    SocketError,
    BindError,
    ListenError,
};
class TcpServer
{
public:
    TcpServer(const uint16_t &port = defaultport, const std::string &ip = defaultip)
        : listensock_(defaultfd), port_(port), ip_(ip)
    {
    }
    void Init()
    {
        listensock_ = socket(AF_INET, SOCK_STREAM, 0); // IPv4
        if(listensock_ < 0 ) 
        {
            lg(Fatal , "create listensock_ ,errno : %d errstring  : %s" , errno , strerror(errno) );
            exit(SocketError ) ;
        }
         lg(Info , "create listensock_  success %d" , listensock_);

        struct sockaddr_in local ;
        memset(&local ,0 ,sizeof (local));
        local.sin_family = AF_INET ;
        local.sin_port = htons(port_) ;
        inet_aton(ip_.c_str() , &(local.sin_addr ) );
           // local.sin_addr.s_addr = INADDR_ANY;
        if (   bind ( listensock_ , ( struct sockaddr *)&local , sizeof (local)) < 0) 
        {
            lg( Fatal , "bind error , error : %d , errstring  : %s " , errno, strerror(errno));
            exit(BindError);
        }

        lg( Info , "bind   listensock_ success , sockfd  %d " , listensock_ ) ; 

        //监听
        if( listen(listensock_ , backlog)  < 0 )
        {
                 lg( Fatal , "listen error , error : %d , errstring  : %s " , errno, strerror(errno));
            exit(ListenError);
        }

           lg( Info , "listen   listensock_ success , sockfd  %d " , listensock_ ) ; 

    }

    void Start ()
    {
        lg( Info , " tcp is running ") ;
        for( ; ; )
        {
            //获取新连接

            struct sockaddr_in client ;
            socklen_t   len =   sizeof(client);
          int sockfd =   accept(listensock_  ,(struct sockaddr *)&client ,&len ) ;
        if( sockfd < 0)   //accept出错，继续下一个
        {
            lg(Warning , "accept error , error : %d , errstring  : %s " , errno, strerror(errno));
            continue ; 
        }
            uint16_t clientport = ntohs(client.sin_port );
            char clientip[32] ;

            inet_ntop(AF_INET , &(client.sin_addr), clientip ,sizeof(clientip) ) ; //将整数IP转换成字符串IP

         lg( Info , "link   socket success , sockfd  %d  ipstr : %s , clientport  : %d" , sockfd , clientip,  clientport) ; 

            //根据新连接来进行通信
        //多进程 

              pid_t id = fork() ;//第一个fork

          
                if( id ==0)
                {
                      //child

                    //父进程打开的sockfd ，子进程也继承了sockfd，子进程也能看见listensock_
                    //子进程不需要listensock_，只需要sockfd
                    close(listensock_);

                    if(fork( ) > 0)  exit(0) ;  //第二个fork


                    //第二个fork ()的返回值为0  ，第二个fork出来的子进程，执行后面的代码
                    //也就是第一个fork的子进程的子进程，执行Service
                     Service ( sockfd , clientip ,clientport) ;
                     close (sockfd);
                    exit(0) ;
                }
            //father
            close( sockfd) ;
        pid_t rid = waitpid (id  ,nullptr , 0) ;
            (void )rid;//避免编译器警告，同时表明不使用 waitpid 的返回值
        }


    }


    void  Service (int sockfd , const std::string & clientip , const uint16_t & clientport)
    {
            char buffer [1024] ;
        while(true)
        {
             ssize_t n =    read(sockfd , buffer,sizeof(buffer));

            if(n>0)
            {
                buffer[n] =0 ; //把buffer当成字符串处理 
                std::cout<<"client say : # " <<buffer <<std::endl;
                 std::string echo_string = "tcpserver echo #";
             echo_string +=buffer;
             write(sockfd , echo_string.c_str(),echo_string.size()); 
            }

            //如果客户端退出 ，服务器就会读到0
            else if (n==0) 
            {
                lg( Info , "%s : %d quit , server close sockfd %d" , clientip.c_str(),clientport,sockfd) ;
                break;
            }

            else 
            {
                 lg( Warning , "read error , sockfd, %d  clientip: %s clientport: %d" , sockfd, clientip.c_str()     ,clientport);
                 break;
            }


        }




    }
    ~TcpServer()
    {
    }

private:
    int listensock_;
    uint16_t port_;  // 端口
    std::string ip_; // ip
};